#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include "locker.h"
#include "threadpool.h"
#include "http_conn.h"

#define MAX_FD 65536   // 最大的文件描述符个数
#define MAX_EVENT_NUMBER 10000  // 监听的最大的事件数量

// 添加文件描述符
extern void addfd( int epollfd, int fd, bool one_shot );
// 移除文件描述符
extern void removefd( int epollfd, int fd );
// 添加信号
extern void addsig(int sig, void( handler )(int));

int main( int argc, char* argv[] ) {
    
    if( argc <= 1 ) {
        printf( "usage: %s port_number\n", basename(argv[0]));
        return 1;
    }

    //设置端口号，ascii to integer
    int port = atoi( argv[1] );
    
    //忽略 SIGPIPE 信号，Broken pipe 向一个没有读端的管道写数据 
    addsig( SIGPIPE, SIG_IGN );

    //创建线程池
    threadpool< http_conn >* pool = NULL;
    try {
        // 有可能出现错误的代码
        pool = new threadpool<http_conn>;
    } catch( ... ) {
        //出错后的处理
        return 1;
    }

    //创建用户数组
    http_conn* users = new http_conn[ MAX_FD ];

    //建立socket连接
    int listenfd = socket( PF_INET, SOCK_STREAM, 0 );
    if (listenfd == -1){
        perror("listenfd");
        exit(-1);
    }

    //创建本地IP和端口的结构体
    int ret = 0;
    struct sockaddr_in address;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_family = AF_INET;
    address.sin_port = htons( port );
    
    // 端口复用
    int reuse = 1;
    setsockopt( listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof( reuse ) );

    ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) );
    if (ret == -1){
        perror("listenfd");
        exit(-1); 
    }

    ret = listen(listenfd, 5);
    if (ret == -1){
        perror("listenfd");
        exit(-1); 
    }

    // 创建epoll对象，和事件数组，添加
    epoll_event events[ MAX_EVENT_NUMBER ];
    int epollfd = epoll_create( 5 );

    // 添加到epoll对象中
    addfd( epollfd, listenfd, false );
    http_conn::m_epollfd = epollfd;

    while(true) {
        
        int number = epoll_wait( epollfd, events, MAX_EVENT_NUMBER, -1 );
        // EINTR：系统调用中断
        if ( ( number < 0 ) && ( errno != EINTR ) ) {
            printf( "epoll failure\n" );
            break;
        }

        for ( int i = 0; i < number; i++ ) {
            
            int sockfd = events[i].data.fd;
            
            if( sockfd == listenfd ) {
                
                struct sockaddr_in client_address;
                socklen_t client_addrlength = sizeof( client_address );
                int connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength );
                
                if ( connfd < 0 ) {
                    printf( "errno is: %d\n", errno );
                    continue;
                } 

                if( http_conn::m_user_count >= MAX_FD ) {
                    close(connfd);
                    continue;
                }
                users[connfd].init( connfd, client_address);

            } else if( events[i].events & ( EPOLLRDHUP | EPOLLHUP | EPOLLERR ) ) {
                //出错
                users[sockfd].close_conn();

            } else if(events[i].events & EPOLLIN) {
                //有数据写入，read()读取数据
                if(users[sockfd].read()) {
                    pool->append(users + sockfd);
                } else {
                    users[sockfd].close_conn();
                }

            }  else if( events[i].events & EPOLLOUT ) {
                //对端读取了一些数据，又重新可写了
                if( !users[sockfd].write() ) {
                    users[sockfd].close_conn();
                }

            }
        }
    }
    
    close( epollfd );
    close( listenfd );
    delete [] users;
    delete pool;
    return 0;

}
